﻿using System;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace SAP.ByDesign.PrintManager.Printing
{
    public class PrintService
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private ExecutionController ec = new ExecutionController();
        private PrintConfiguration pc = new PrintConfiguration();

        public void Start()
        {
            log.Info("Starting printing service...");

            // create a retrieve task and start it immediately
            new RetrieveTask(ec, pc, "https://bing.com/");
            new RetrieveTask(ec, pc, "https://stackoverflow.com/");
            new RetrieveTask(ec, pc, "https://google.com/");
            new RetrieveTask(ec, pc, "https://microsoft.com/");

            log.Info("Printing service has been successfully started");
        }

        public void Stop()
        {
            log.Info("Stopping printing service...");

            // Send terminating signal to all workers
            ec.Cancel();

            // Wait
            if (ec.Wait(15000))
            {
                log.Info("Printing service has been gracefully stopped");
            }
            else
            {
                ec.Terminate();
                log.Info("Printing service has been brutally terminated");
            }
        }
    }

    class RetrieveTask
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        private readonly ExecutionController _ec;
        private readonly PrintConfiguration _pc;
        private readonly string _targetUrl;

        public RetrieveTask(ExecutionController ec, PrintConfiguration pc, string targetUrl)
        {
            _ec = ec;
            _pc = pc;
            _targetUrl = targetUrl;

            new Task(Run, ec.Token).Start();
        }

        private void Run(object executor)
        {
            if (_ec.Token.IsCancellationRequested)
            {
                log.Info("cancellation is requested");
                return;
            }

            _ec.SetTracer();

            var request = (HttpWebRequest)WebRequest.Create(_targetUrl);
            log.Info(" request -> " + _targetUrl);

            Task.Factory.FromAsync<WebResponse>(
                request.BeginGetResponse, request.EndGetResponse, null, TaskCreationOptions.None)
                .ContinueWith(ResponseCompleted);

            _ec.ClearTracer();
        }

        private void ResponseCompleted(Task<WebResponse> task)
        {
            _ec.SetTracer();

            WebResponse webResponse = null;
            try
            {
                webResponse = task.Result;
                log.Info("  response -> " + webResponse.ResponseUri);


                //new RetrieveTask(_ec, _pc, task);
                task.ContinueWith(Run);
            }
            catch (AggregateException ae)
            {
                if (ae.GetBaseException() is WebException)
                    log.Error(" failed: " + ae.GetBaseException().Message);
                else throw;
            }
            finally
            {
                if (webResponse != null) webResponse.Close();

                _ec.ClearTracer();
            }
        }

    }

    class PrintConfiguration
    {
    }

    class ExecutionController
    {
        // used to trace thread state
        private readonly int[] _tracers = new int[32];
        private readonly CancellationTokenSource _cts = new CancellationTokenSource();

        public ExecutionController()
        {
           
        }

        public CancellationToken Token
        {
            get { return _cts.Token; }
        }

        public void SetTracer()
        {
            Thread.VolatileWrite(ref _tracers[Thread.CurrentThread.ManagedThreadId], 1);
        }

        public void ClearTracer()
        {
            Thread.VolatileWrite(ref _tracers[Thread.CurrentThread.ManagedThreadId], 0);
        }

        public void Cancel()
        {
            _cts.Cancel();
        }

        public bool Wait(int millisecondsTimeout)
        {
            const int piece = 100;
            int ellapsed = 0;

            while (ellapsed < millisecondsTimeout)
            {
                int counter = 0;
                for (int i = 0; i < _tracers.Length; i++)
                {
                    int tracer = Thread.VolatileRead(ref _tracers[i]);
                    counter += tracer;

                    if (counter > 0)
                    {
                        break;
                    }
                }

                if (counter > 0)
                {
                    ellapsed += piece;
                }
                else
                {
                    return true;
                }
            }

            return false;
        }

        public void Terminate()
        {
            // There is no way to abort the threads created by ThreadPool,
            // we just do nothing but return
        }
    }
}
